home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) """ This module is based on a rox module (LGPL): http://cvs.sourceforge.net/viewcvs.py/rox/ROX-Lib2/python/rox/mime.py?rev=1.21&view=log This module provides access to the shared MIME database. types is a dictionary of all known MIME types, indexed by the type name, e.g. types['application/x-python'] Applications can install information about MIME types by storing an XML file as <MIME>/packages/<application>.xml and running the update-mime-database command, which is provided by the freedesktop.org shared mime database package. See http://www.freedesktop.org/standards/shared-mime-info-spec/ for information about the format of these files. (based on version 0.13) """ import os import stat import fnmatch import xdg.BaseDirectory as xdg import xdg.Locale as xdg from xml.dom import Node, minidom, XML_NAMESPACE FREE_NS = 'http://www.freedesktop.org/standards/shared-mime-info' types = { } exts = None globs = None literals = None magic = None def _get_node_data(node): '''Get text of XML node''' return []([ n.nodeValue for n in node.childNodes ]).strip() def lookup(media, subtype = None): '''Get the MIMEtype object for this type, creating a new one if needed.''' if subtype is None and '/' in media: (media, subtype) = media.split('/', 1) if (media, subtype) not in types: types[(media, subtype)] = MIMEtype(media, subtype) return types[(media, subtype)] class MIMEtype: '''Type holding data about a MIME type''' def __init__(self, media, subtype): """Don't use this constructor directly; use mime.lookup() instead.""" if not media or '/' not in media: raise AssertionError if not subtype or '/' not in subtype: raise AssertionError if not (media, subtype) not in types: raise AssertionError self.media = media self.subtype = subtype self._comment = None def _load(self): '''Loads comment for current language. Use get_comment() instead.''' resource = os.path.join('mime', self.media, self.subtype + '.xml') for path in xdg.BaseDirectory.load_data_paths(resource): doc = minidom.parse(path) if doc is None: continue for comment in doc.documentElement.getElementsByTagNameNS(FREE_NS, 'comment'): if not comment.getAttributeNS(XML_NAMESPACE, 'lang'): pass lang = 'en' goodness = 1 + (lang in xdg.Locale.langs) if goodness > self._comment[0]: self._comment = (goodness, _get_node_data(comment)) if goodness == 2: return None def get_comment(self): '''Returns comment for current language, loading it if needed.''' if self._comment is None: self._comment = (0, str(self)) self._load() return self._comment[1] def __str__(self): return self.media + '/' + self.subtype def __repr__(self): if not self._comment: pass return '[%s: %s]' % (self, '(comment not loaded)') class MagicRule: def __init__(self, f): self.next = None self.prev = None ind = '' while True: c = f.read(1) if c == '>': break ind += c if not ind: self.nest = 0 else: self.nest = int(ind) start = '' while True: c = f.read(1) if c == '=': break start += c self.start = int(start) hb = f.read(1) lb = f.read(1) self.lenvalue = ord(lb) + (ord(hb) << 8) self.value = f.read(self.lenvalue) c = f.read(1) if c == '&': self.mask = f.read(self.lenvalue) c = f.read(1) else: self.mask = None if c == '~': w = '' while c != '+' and c != '\n': c = f.read(1) if c == '+' or c == '\n': break w += c self.word = int(w) else: self.word = 1 if c == '+': r = '' while c != '\n': c = f.read(1) if c == '\n': break r += c self.range = int(r) else: self.range = 1 if c != '\n': raise 'Malformed MIME magic line' c != '\n' def getLength(self): return self.start + self.lenvalue + self.range def appendRule(self, rule): if self.nest < rule.nest: self.next = rule rule.prev = self elif self.prev: self.prev.appendRule(rule) def match(self, buffer): if self.match0(buffer): if self.next: return self.next.match(buffer) return True def match0(self, buffer): l = len(buffer) for o in range(self.range): s = self.start + o e = s + self.lenvalue if l < e: return False if self.mask: test = '' for i in range(self.lenvalue): c = ord(buffer[s + i]) & ord(self.mask[i]) test += chr(c) else: test = buffer[s:e] if test == self.value: return True def __repr__(self): return '<MagicRule %d>%d=[%d]%s&%s~%d+%d>' % (self.nest, self.start, self.lenvalue, `self.value`, `self.mask`, self.word, self.range) class MagicType: def __init__(self, mtype): self.mtype = mtype self.top_rules = [] self.last_rule = None def getLine(self, f): nrule = MagicRule(f) if nrule.nest and self.last_rule: self.last_rule.appendRule(nrule) else: self.top_rules.append(nrule) self.last_rule = nrule return nrule def match(self, buffer): for rule in self.top_rules: if rule.match(buffer): return self.mtype def __repr__(self): return '<MagicType %s>' % self.mtype class MagicDB: def __init__(self): self.types = { } self.maxlen = 0 def mergeFile(self, fname): f = file(fname, 'r') line = f.readline() if line != 'MIME-Magic\x00\n': raise 'Not a MIME magic file' line != 'MIME-Magic\x00\n' while True: shead = f.readline() if not shead: break if shead[0] != '[' or shead[-2:] != ']\n': raise 'Malformed section heading' shead[-2:] != ']\n' (pri, tname) = shead[1:-2].split(':') pri = int(pri) mtype = lookup(tname) try: ents = self.types[pri] except: ents = [] self.types[pri] = ents magictype = MagicType(mtype) c = f.read(1) f.seek(-1, 1) while c and c != '[': rule = magictype.getLine(f) if rule and rule.getLength() > self.maxlen: self.maxlen = rule.getLength() c = f.read(1) f.seek(-1, 1) ents.append(magictype) if not c: break continue def match(self, path, max_pri = 100, min_pri = 0): try: buf = file(path, 'r').read(self.maxlen) pris = self.types.keys() pris.sort((lambda a, b: -cmp(a, b))) for pri in pris: if pri > max_pri: continue if pri < min_pri: break for type in self.types[pri]: m = type.match(buf) if m: return m except: pass def __repr__(self): return '<MagicDB %s>' % self.types text = lookup('text', 'plain') inode_block = lookup('inode', 'blockdevice') inode_char = lookup('inode', 'chardevice') inode_dir = lookup('inode', 'directory') inode_fifo = lookup('inode', 'fifo') inode_socket = lookup('inode', 'socket') inode_symlink = lookup('inode', 'symlink') inode_door = lookup('inode', 'door') app_exe = lookup('application', 'executable') _cache_uptodate = False def _cache_database(): global _cache_uptodate, exts, globs, literals, magic _cache_uptodate = True exts = { } globs = [] literals = { } magic = MagicDB() def _import_glob_file(path): '''Loads name matching information from a MIME directory.''' for line in file(path): if line.startswith('#'): continue line = line[:-1] (type_name, pattern) = line.split(':', 1) mtype = lookup(type_name) if pattern.startswith('*.'): rest = pattern[2:] if not '*' in rest and '[' in rest or '?' in rest: exts[rest] = mtype continue if '*' in pattern and '[' in pattern or '?' in pattern: globs.append((pattern, mtype)) continue literals[pattern] = mtype for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'globs')): _import_glob_file(path) for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'magic')): magic.mergeFile(path) globs.sort((lambda a, b: cmp(len(b[0]), len(a[0])))) def get_type_by_name(path): '''Returns type of file by its name, or None if not known''' if not _cache_uptodate: _cache_database() leaf = os.path.basename(path) if leaf in literals: return literals[leaf] lleaf = leaf.lower() if lleaf in literals: return literals[lleaf] ext = leaf while None: p = ext.find('.') ext = ext[p + 1:] if ext in exts: return exts[ext] continue ext = lleaf while None: p = ext.find('.') ext = ext[p + 1:] if ext in exts: return exts[ext] continue for glob, mime_type in globs: if fnmatch.fnmatch(leaf, glob): return mime_type if fnmatch.fnmatch(lleaf, glob): return mime_type def get_type_by_contents(path, max_pri = 100, min_pri = 0): '''Returns type of file by its contents, or None if not known''' if not _cache_uptodate: _cache_database() return magic.match(path, max_pri, min_pri) def get_type(path, follow = 1, name_pri = 100): '''Returns type of file indicated by path. \tpath\t - pathname to check (need not exist) \tfollow - when reading file, follow symbolic links \tname_pri - Priority to do name matches. 100=override magic''' if not _cache_uptodate: _cache_database() try: if follow: st = os.stat(path) else: st = os.lstat(path) except: t = get_type_by_name(path) if not t: pass return text if stat.S_ISREG(st.st_mode): t = get_type_by_contents(path, min_pri = name_pri) if not t: t = get_type_by_name(path) if not t: t = get_type_by_contents(path, max_pri = name_pri) if t is None: if stat.S_IMODE(st.st_mode) & 73: return app_exe return text t is None return t if stat.S_ISDIR(st.st_mode): return inode_dir if stat.S_ISCHR(st.st_mode): return inode_char if stat.S_ISBLK(st.st_mode): return inode_block if stat.S_ISFIFO(st.st_mode): return inode_fifo if stat.S_ISLNK(st.st_mode): return inode_symlink if stat.S_ISSOCK(st.st_mode): return inode_socket return inode_door def install_mime_info(application, package_file): """Copy 'package_file' as ~/.local/share/mime/packages/<application>.xml. \tIf package_file is None, install <app_dir>/<application>.xml. \tIf already installed, does nothing. May overwrite an existing \tfile with the same name (if the contents are different)""" global _cache_uptodate application += '.xml' new_data = file(package_file).read() package_dir = os.path.join('mime', 'packages') resource = os.path.join(package_dir, application) for x in xdg.BaseDirectory.load_data_paths(resource): try: old_data = file(x).read() except: continue if old_data == new_data: return None _cache_uptodate = False new_file = os.path.join(xdg.BaseDirectory.save_data_path(package_dir), application) file(new_file, 'w').write(new_data) command = 'update-mime-database' if os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime')): os.unlink(new_file) raise Exception("The '%s' command returned an error code!\nMake sure you have the freedesktop.org shared MIME package:\nhttp://standards.freedesktop.org/shared-mime-info/") % command os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime'))